package com.jintian.smart.kernel.flowable.component;

import com.alibaba.fastjson.JSON;
import com.jintian.smart.kernel.common.util.StringUtil;
import com.jintian.smart.kernel.core.auth.IOperatorProvider;
import com.jintian.smart.kernel.core.flow.FlowAction;
import com.jintian.smart.kernel.core.flow.IProcessContext;
import com.jintian.smart.kernel.core.flow.ProcessControl;
import com.jintian.smart.kernel.flowable.context.DefaultProcessContext;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.ProcessEngine;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.TaskInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import java.io.Closeable;
import java.io.IOException;
import java.util.List;
import java.util.Stack;

@Slf4j
@Component
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class ProcessContextProvider {
    @Autowired
    private IOperatorProvider operatorProvider;
    @Autowired
    private ProcessEngine processEngine;

    private static final ThreadLocal<Stack<IProcessContext<List<TaskInfo>>>> runtimeInstance = new ThreadLocal<Stack<IProcessContext<List<TaskInfo>>>>() {
        @Override
        protected Stack<IProcessContext<List<TaskInfo>>> initialValue() {
            return new Stack<>();
        }
    };

    public IProcessContext<List<TaskInfo>> createInstance(String process, String activity, FlowAction action,
                                                          String processInstanceId, String taskId, String businessKey, ProcessControl processControl) {
        return this.createInstance(process, activity, action, processInstanceId, taskId, businessKey, processControl, false);
    }

    public IProcessContext<List<TaskInfo>> createInstance(String process, String activity, FlowAction action, String processInstanceId, String taskId,
                                                          String businessKey, ProcessControl processControl, boolean replaceOld) {
        Assert.isTrue(StringUtil.isNoneBlank(businessKey), "流程关联业务主键不能为空");
        DefaultProcessContext impl = new DefaultProcessContext();
        impl.setFireClosed(new Closeable() {
            @Override
            public void close() throws IOException {
                Assert.isTrue(impl.equals(runtimeInstance.get().peek()), "级联流程控制逻辑错误,请检查代码");
                runtimeInstance.get().pop();
            }
        });
        impl.setProcess(process);
        impl.setActivity(activity);
        impl.setAction(action);
        impl.setBusinessKey(businessKey);
        if (StringUtil.isNotEmpty(processInstanceId)) {
            ProcessInstance pi = this.processEngine.getRuntimeService().createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
            impl.setProcessLabel(pi.getName());
            impl.setProcessVersion(pi.getProcessDefinitionVersion());
        } else if (StringUtil.isNotEmpty(taskId)) {
            processInstanceId = this.processEngine.getTaskService().createTaskQuery().taskId(taskId).singleResult().getProcessInstanceId();
            ProcessInstance pi = this.processEngine.getRuntimeService().createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
            impl.setProcessVersion(pi.getProcessDefinitionVersion());
            impl.setProcessLabel(pi.getName());
        } else if (StringUtil.isNotEmpty(process)) {
            ProcessDefinition pd = this.processEngine.getRepositoryService().createProcessDefinitionQuery().processDefinitionKey(process).latestVersion().singleResult();
            // org.flowable.bpmn.model.Process mainProcess = this.processEngine.getRepositoryService().getBpmnModel(pd.getId()).getMainProcess();
            // TODO pd.getName()
            impl.setProcessLabel(pd.getName());
            impl.setProcessVersion(pd.getVersion());
        }
        impl.setProcessInstanceId(processInstanceId);
        impl.setTaskId(taskId);
        impl.setOperator(operatorProvider.currentOperator());
        if (processControl != null) {
            impl.setProcessControl(processControl);
            impl.getVariables().putAll(processControl.getVariables());
            impl.getTransientVariables().putAll(processControl.getTransientVariables());
            // 将自己压入到非持久变量，用于表达式计算
            impl.getTransientVariables().put("context", impl);
        }

        Stack<IProcessContext<List<TaskInfo>>> stack = runtimeInstance.get();
        if (stack.contains(impl)) {
            if (replaceOld) {
                int idx = stack.indexOf(impl);
                IProcessContext<List<TaskInfo>> old = stack.remove(idx);
                stack.add(idx, impl);
                log.warn("编码强制替换流程环境,old:{},new:{}", JSON.toJSONString(old), JSON.toJSONString(impl));
            } else {
                throw new IllegalArgumentException("流程操作环境已存在:[" + impl.getKey() + "]");
            }
        } else {
            stack.push(impl);
        }
        return impl;
    }

    public IProcessContext<List<TaskInfo>> currentProcessContext() {
        return ProcessContextProvider.getCurrentProcessContext();
    }

    public static DefaultProcessContext getCurrentProcessContext() {
        Stack<IProcessContext<List<TaskInfo>>> stack = runtimeInstance.get();
        Assert.isTrue(stack.size() > 0, "未创建流程环境");
        return (DefaultProcessContext) stack.peek();
    }
}
